package xw4g08;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.openrdf.model.Literal;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.ValueFactory;
import org.openrdf.model.impl.ValueFactoryImpl;
import org.openrdf.rio.ParseLocationListener;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.RDFHandler;
import org.openrdf.rio.RDFHandlerException;
import org.openrdf.rio.RDFParseException;
import org.openrdf.rio.RDFParser;
import org.openrdf.rio.Rio;
import org.openrdf.rio.helpers.RDFParserBase;
import org.openrdf.rio.ntriples.NTriplesUtil;
import org.openrdf.rio.turtle.TurtleWriter;
public class Splitter {

	static File tempDir = Configuration.BASE_DIR;
	static File repDir = new File(tempDir, "repository").getAbsoluteFile();
	static File input = new File("/home/xgfd/Applications/bsbmtools-0.2");//Configuration.IN_DIR;
	static File outPut = Configuration.OUT_DIR;
	static RDFFormat informat = RDFFormat.TURTLE;
	static RDFFormat outformat = RDFFormat.TURTLE;
	
	public static void main(String[] args) {
		
		if(args.length!=0){
			input = new File(args[0]);
		}
		if(!tempDir.exists())
			tempDir.mkdir();
		if(!input.exists())
			input.mkdir();
		if(!repDir.exists())
			repDir.mkdir();
		if(!outPut.exists())
			outPut.mkdir();
		
		/*File[] files = outPut.listFiles();
		for(int i = 0;i < files.length;i++){
			files[i].delete();
		}*/
		List<File> inFiles = filter(input.listFiles(),informat);
		
		String baseURI = "xw4g08";
		//File inFile = inFiles.get(1);
		Configuration.init();
		int repNum = Configuration.REP_NUM;//how many repositories you want
		RDFParser parser = Rio.createParser(informat);
		//inFiles.remove(0);
		for(File inFile:inFiles) {
			List<RDFHandler> handlers = new ArrayList<RDFHandler>();
			try {
				for(int i=0;i<repNum;i++) {
					
					String[] temp = inFile.getName().split(".ttl");
					File outFile = new File(outPut + File.separator + temp[0]);
					if(!outFile.exists()) {
						outFile.mkdirs();
					}
					FileOutputStream out = new FileOutputStream(outFile.getAbsolutePath() + File.separator + "splitted_" + (i+1) +".ttl");
					RDFHandler handler = Rio.createWriter(outformat,out);
					handlers.add(handler);
				}
				InputStream in = new FileInputStream(inFile);
				RDFHandler muloutputhandler = new MultiOutputHandler(handlers);
				parser.setRDFHandler(muloutputhandler);
				parser.setParseLocationListener(new Listener());
				System.out.println("Start processing "+inFile);
				parser.parse(in, baseURI);
				
			} catch (FileNotFoundException e) {
					e.printStackTrace();
				} catch (RDFHandlerException e) {
					e.printStackTrace();
				} catch (RDFParseException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
		
		System.out.println("Done");
	}
	
	public static List<File> filter(File[] inputs, RDFFormat type) {
		List<File> RDFFiles = new ArrayList<File>();
		for(File f:inputs) {
			if(Rio.getParserFormatForFileName(f.getName())!=null && Rio.getParserFormatForFileName(f.getName()).equals(type)) {
				RDFFiles.add(f);
			}
		}
		return RDFFiles;
	}
	
	
	//get the type of the input file
	public static RDFFormat getFileType(String file){
		file = file.toLowerCase();
		if(file.endsWith(".rdf"))
			return RDFFormat.RDFXML;
		if(file.endsWith(".n3"))
			return RDFFormat.N3;
		if(file.endsWith(".ttl"))
			return RDFFormat.TURTLE;
		if(file.endsWith(".tig")||file.endsWith(".trig"))
			return RDFFormat.TRIG;
		if(file.endsWith(".nt"))
			return RDFFormat.NTRIPLES;
		if(file.endsWith(".trix"))
			return RDFFormat.TRIX;
		System.out.println("Error:not a supported format.");
		return null;
	}
}


class MultiOutputHandler implements RDFHandler {

	private List<RDFHandler> handlers;
	private int id=0;
	private Random ran = new Random();
	private Map<RDFHandler,Long> counter = new HashMap<RDFHandler,Long>();
	
	MultiOutputHandler(List<RDFHandler> handlers) {
		this.handlers = handlers;
		for(RDFHandler h:handlers) {
			counter.put(h,0L);
		}
	}
	
	
	@Override
	public void startRDF() throws RDFHandlerException {
		for(RDFHandler handler:handlers) {
			handler.startRDF();
		}
	}

	@Override
	public void endRDF() throws RDFHandlerException {
		for(int i=0;i<handlers.size();i++) {
			RDFHandler h = handlers.get(i);
			h.endRDF();
			System.out.println("Part "+(i+1)+" has "+counter.get(h)+" triples.");
		}
	}

	@Override
	public void handleNamespace(String prefix, String uri)
			throws RDFHandlerException {
		System.out.println("Processing prefixes.");
		for(RDFHandler handler:handlers) {
			handler.handleNamespace(prefix,uri);
		}
	}

	@Override
	public void handleStatement(Statement st) throws RDFHandlerException {
		id = nextID();
		RDFHandler h = handlers.get(id);
		System.out.println(id);
		h.handleStatement(st);
		counter.put(h,counter.get(h)+1);
		if(counter.get(h)%10000 == 0) {
			System.out.println(counter.get(h)+" triples have been written to part "+(id+1));
		}
	}

	@Override
	public void handleComment(String comment) throws RDFHandlerException {
		handlers.get(id).handleComment(comment);
	}
	
	private int nextID() {
		int repID;
		double scale = handlers.size()/3;//for Gaussian. 99.730020% numbers are within 3, so scale 3 to repNum
		while(true){
			repID = (int) Math.floor(Math.abs(scale*ran.nextGaussian()));//get the repository's ID
			if(repID < handlers.size())//if gau is within the bound,keep gau,otherwise get the next gau
				break;
		}
		return repID;
	}
}



class Listener implements ParseLocationListener {

	@Override
	public void parseLocationUpdate(int lineNo, int columnNo) {
		if(lineNo%10000 == 0) {
			System.out.println(lineNo + " lines processed.");
		}
		
	}
	
}
